using System.Collections;
using System.Collections.Specialized;
using System.Linq;
using System.Windows.Controls.Primitives;

namespace System.Windows.Controls
{
    ///<summary>
    /// Display hierarchical data in customizable grid
    ///</summary>
    public class TreeGrid : DataGrid
    {
        ///<summary>
        /// Identifies the <see cref="ItemsSource"/> dependency property
        ///</summary>
        public new static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof (IEnumerable), typeof (TreeGrid),
                                        new PropertyMetadata(ItemsSourceChanged));

        private static void ItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var grid = (TreeGrid) d;
            var newVal = (IEnumerable) e.NewValue;
            var oldVal = (IEnumerable) e.OldValue;
            if (oldVal is INotifyCollectionChanged)
                ((INotifyCollectionChanged) oldVal).CollectionChanged -= grid.DataCollectionChanged;
            if (newVal is INotifyCollectionChanged)
                ((INotifyCollectionChanged) newVal).CollectionChanged += grid.DataCollectionChanged;
            grid.ClearData();
            grid.InitData();
        }

        ///<summary>
        /// Identifies the <see cref="SelectedItem"/> dependency property
        ///</summary>
        public new static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register("SelectedItem", typeof (TreeGridItem), typeof (TreeGrid),
                                        new PropertyMetadata(OnSelectedItemChanged));

        private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var grid = (TreeGrid) d;
            var item = (TreeGridItem) e.NewValue;
            grid.SelectedValue = item == null ? null : item.Data;
        }

        ///<summary>
        /// Identifies the <see cref="SelectedValue"/> dependency property
        ///</summary>
        public static readonly DependencyProperty SelectedValueProperty =
            DependencyProperty.Register("SelectedValue", typeof (object), typeof (TreeGrid),
                                        new PropertyMetadata(OnSelectedValueChanged));

        private static void OnSelectedValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((TreeGrid) d).SetBaseSelectedItem(e.NewValue);
        }

        private void SetBaseSelectedItem(object value)
        {
            base.SelectedItem = value;
        }

        internal const double HolderWidthPerLevel = 20.0;


        ///<summary>
        /// Initializes a new instance of the <see cref="TreeGrid"/> class
        ///</summary>
        public TreeGrid()
        {
            _items = new TreeGridItemCollection();
            _items.SetParentGrid(this);
            _items.CollectionChanged +=
                (sender, e) =>
                    {
                        switch (e.Action)
                        {
                            case NotifyCollectionChangedAction.Add:
                                ItemsSource.InserItemInSource(e.NewStartingIndex, ((TreeGridItem) e.NewItems[0]).Data);
                                break;
                            case NotifyCollectionChangedAction.Remove:
                                ItemsSource.RemoveItemFromSource(e.OldStartingIndex, ((TreeGridItem) e.OldItems[0]).Data);
                                break;
                            case NotifyCollectionChangedAction.Replace:
                                break;
                            case NotifyCollectionChangedAction.Reset:
                                break;
                        }
                    };
            Columns.CollectionChanged +=
                (sender, e) =>
                    {
                        if (e.Action == NotifyCollectionChangedAction.Add)
                            foreach (var column in e.NewItems.OfType<IHierarchicalColumn>())
                                Column = column;
                        if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Contains(Column))
                            Column = null;
                    };

            base.ItemsSource = (_datasource = new ManagedDataGridSource(this, _items));
        }

        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            SelectedValue = base.SelectedItem;
            SelectedItem = _items.GetItemForData(SelectedValue);
            base.OnSelectionChanged(new SelectionChangedEventArgs(
                                        e.RemovedItems.Cast<object>().Select(obj => _items.GetItemForData(obj)).ToList(),
                                        e.AddedItems.Cast<object>().Select(obj => _items.GetItemForData(obj)).ToList()));
        }

        private readonly ManagedDataGridSource _datasource;

        internal ManagedDataGridSource DataSource { get { return _datasource; } }

        private IHierarchicalColumn _column;

        internal IHierarchicalColumn Column
        {
            get { return _column; }
            set
            {
                if (_column != null && value != null) throw new InvalidOperationException();
                if (_column != null)
                    _column.ParentGrid = null;
                _column = value;
                if (_column == null) return;
                _column.ParentGrid = this;
            }
        }

        private ScrollBar _verticalScrollbar;

        internal ScrollBar VerticalScrollbar { get { return _verticalScrollbar; } }

        private ScrollBar _horizontalScrollbar;

        internal ScrollBar HorizontalScrollbar { get { return _horizontalScrollbar; } }

        public override void OnApplyTemplate()
        {
            _verticalScrollbar = GetTemplateChild("VerticalScrollbar") as ScrollBar;
            _horizontalScrollbar = GetTemplateChild("HorizontalScrollbar") as ScrollBar;

            base.OnApplyTemplate();
        }

        /// <summary>
        /// Gets or sets collection that is used to generate the content of the control
        /// </summary>
        public new IEnumerable ItemsSource
        {
            get { return (IEnumerable) GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        /// <summary>
        /// Gets or sets current selected <see cref="TreeGridItem"/>
        /// </summary>
        public new TreeGridItem SelectedItem
        {
            get { return (TreeGridItem) GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        /// <summary>
        /// Gets or sets the data item corresponding to the selected item
        /// </summary>
        public object SelectedValue
        {
            get { return GetValue(SelectedValueProperty); }
            set { SetValue(SelectedValueProperty, value); }
        }

        protected override void OnLoadingRow(DataGridRowEventArgs e)
        {
            SetRow(e.Row);

            base.OnLoadingRow(e);
        }

        #region beh
        private void DataCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    _items.InsertInt(e.NewStartingIndex, new TreeGridItem {Data = e.NewItems[0], IsExpanded = Column.AutoExpand});
                    break;
                case NotifyCollectionChangedAction.Remove:
                    _items.RemoveInt(Items.GetItemForData(e.OldItems[0]));
                    break;
            }
        }

        private void ClearData()
        {
            _items.ClearInt();
        }

        private void InitData()
        {
            if (ItemsSource == null)
            {
                _datasource.Refresh();
                return;
            }
            foreach (var obj in ItemsSource)
                _items.AddInt(new TreeGridItem {Data = obj, IsExpanded = Column.AutoExpand});
        }

        private readonly TreeGridItemCollection _items;

        /// <summary>
        /// Gets the collection of root tree nodes
        /// </summary>
        public TreeGridItemCollection Items { get { return _items; } }

        internal void SetRow(DataGridRow row)
        {
            var lastOwner = Items.GetItemForRow(row);
            if (lastOwner != null) lastOwner.SetRowAndHolder(null, null);
            TreeHolder holder = null;
            if (Column != null)
            {
                var grid = (Grid) Columns[Columns.IndexOf((DataGridColumn) Column)].GetCellContent(row);
                if (grid != null)
                    holder = grid.Children[0] as TreeHolder;
            }
            _items.GetItemForData(row.DataContext).SetRowAndHolder(row, holder);
        }

        internal FrameworkElement Generate(DataGridCell cell, object dataItem, FrameworkElement element)
        {
            var grid = new Grid();
            grid.ColumnDefinitions.Add(new ColumnDefinition {Width = GridLength.Auto});
            grid.ColumnDefinitions.Add(new ColumnDefinition());
            grid.Children.Add(new TreeHolder());
            if (element != null)
            {
                element.SetValue(Grid.ColumnProperty, 1);
                grid.Children.Add(element);
            }
            return grid;
        }

        /// <summary>
        /// Refresh sortings and filtrations
        /// </summary>
        public void Refresh()
        {
            _datasource.UpdateState();
        }
        #endregion

        //public event EventHandler<TreeGridItemEventArgs> ItemConnect;

        //internal void InvokeItemConnect(TreeGridItem item)
        //{
        //    var handler = ItemConnect;
        //    if (handler != null) handler(this, new TreeGridItemEventArgs(item));
        //}

        //public event EventHandler<TreeGridItemEventArgs> ItemDisconnect;

        //internal void InvokeItemDisconnect(TreeGridItem item)
        //{
        //    var handler = ItemDisconnect;
        //    if (handler != null) handler(this, new TreeGridItemEventArgs(item));
        //}

        public TreeGridItem GetItemForData(object data)
        {
            return Items.GetItemForData(data);
        }
    }
}